package org.zjvis.datascience.web.controller;

import cn.weiguangfu.swagger2.plus.annotation.ApiPlus;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.zjvis.datascience.common.annotation.ProjectAuth;
import org.zjvis.datascience.common.dto.PipelineDTO;
import org.zjvis.datascience.common.dto.ProjectDatasetDTO;
import org.zjvis.datascience.common.dto.TaskDTO;
import org.zjvis.datascience.common.widget.dto.WidgetDTO;
import org.zjvis.datascience.common.enums.ProjectAuthEnum;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.formula.vo.FormulaHistoryVO;
import org.zjvis.datascience.common.formula.vo.FormulaIDVO;
import org.zjvis.datascience.common.formula.vo.FormulaRequestVO;
import org.zjvis.datascience.common.formula.vo.FormulaVO;
import org.zjvis.datascience.common.widget.request.PidsComboVOInWidget;
import org.zjvis.datascience.common.widget.request.RelateWidgetRequestVO;
import org.zjvis.datascience.common.widget.request.RelateWidgetResultVO;
import org.zjvis.datascience.common.widget.vo.*;
import org.zjvis.datascience.service.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.stream.Collectors;

import static org.zjvis.datascience.common.constant.DatabaseConstant.GREEN_PLUM_DEFAULT_SCHEMA;
import static org.zjvis.datascience.common.widget.WidgetJsonConstant.PUBLISHED_FLAG;

/**
 * @description 可视化接口 Controller
 * @date 2021-12-24
 */
@ApiPlus(value = true)
@RequestMapping("/widget")
@RestController
@Api(tags = "widget", description = "可视化接口")
@Validated
public class WidgetController {

    @Autowired
    private WidgetService widgetService;

    @Autowired
    private WidgetFavouriteService widgetFavouriteService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private PipelineService pipelineService;

    @Autowired
    private UserProjectService userProjectService;

    @Autowired
    private DatasetProjectService datasetProjectService;

    private static final String OPERATOR_ADD = "add";

    private static final String OPERATOR_UPDATE = "update";

    private final static Logger logger = LoggerFactory.getLogger(WidgetController.class);

    @PostMapping(value = "/save")
    @ResponseBody
    @ApiOperation(value = "新增可视化", notes = "新增可视化")
    public ApiResult<Long> save(HttpServletRequest request, @RequestBody @ProjectAuth @Valid WidgetVO vo) {
        Long userId = JwtUtil.getCurrentUserId();
        //dashboardId 为-1代表是 点击加号创建的， 否则拖拽生成的则隶属于这个画布 需要传入对应dashboardId
        //dashboardId 为 -2 代表 该dashboard 已经被下架，项目中如果有发布了的画板被下架，  只有下架后才能被删除
        WidgetDTO widget = vo.toWidget();
        widget.setGmtCreator(userId);
        widget.setGmtModifier(userId);
        return ApiResult.valueOf(widgetService.save(widget));
    }

    @PostMapping(value = "/update")
    @ResponseBody
    @ApiOperation(value = "更新可视化", notes = "更新可视化")
    public ApiResult<Long> update(HttpServletRequest request, @RequestBody @ProjectAuth @Valid WidgetVO vo) {
        widgetService.update(vo.toWidget());
        return ApiResult.valueOf(vo.getId());
    }

    @PostMapping(value = "/batchUpdate")
    @ResponseBody
    @ApiOperation(value = "批量更新可视化", notes = "批量更新可视化")
    public ApiResult<List<Long>> batchUpdate(HttpServletRequest request,
                                             @RequestBody @ProjectAuth JSONObject params) {
        try {
            List<Long> ids = widgetService.batchUpdate(params.getJSONArray("widgets"));
            return ApiResult.valueOf(ids);
        } catch (Exception e) {
            logger.error("API /widget/batchUpdate failed, since {}", e.getMessage());
            return ApiResult.error(ApiResultCode.SYS_ERROR, e.getMessage());
        }
    }

    /**
     * TODO可视化 返回全部节点信息以及配置信息
     *
     * @param request
     * @return
     */
    @PostMapping(value = "/queryByProject")
    @ResponseBody
    @ApiOperation(value = "查询可视化", notes = "根据projectId查询可视化")
    public ApiResult<List<PipelineWidgetVO>> queryByProject(HttpServletRequest request,
                                                            @ProjectAuth(auth = ProjectAuthEnum.READ) @RequestBody @Valid PidsComboVOInWidget vo) {

        List<PipelineWidgetVO> resultList = Lists.newArrayList();
        //查询项目中引入的dataset节点，及其可视化视图
        List<ProjectDatasetDTO> datasetDTOS = datasetProjectService.queryProjectDataset(vo.getProjectId(), null);
        PipelineWidgetVO datasetWidgetVO = new PipelineWidgetVO(GREEN_PLUM_DEFAULT_SCHEMA);
        List<WidgetPackageVO> datasetWidgets = Lists.newArrayList();
        for (ProjectDatasetDTO dto : datasetDTOS) {
            List<WidgetDTO> addedWidget = widgetService.queryTemplateByDatasetId(dto.getId());
            List<WidgetVO> addWidgetVOs = addedWidget.stream().map(WidgetDTO::view).collect(Collectors.toList());
            addWidgetVOs.addAll(0, dto.toWidgets()); //将原始的数据图表置于首位
            datasetWidgets.add(new WidgetPackageVO(dto.getId(), dto.getDatasetName(), addWidgetVOs));
        }
        //TODO 添加协作pipeline的数据集
        datasetWidgetVO.setTaskWidgets(datasetWidgets);
        resultList.add(datasetWidgetVO);

        //查询pipeline中的全部节点，及其可视化视图 (因为发布后，可能会复制pipeline 造成节点信息不同)
        List<PipelineDTO> pipelineDTOS = pipelineService.queryByProject(vo.getProjectId());
        if (null != vo.getPipelineId()) {
            //发布状态
            for (PipelineDTO pipeline : pipelineDTOS) {
                if (pipeline.getId().equals(vo.getPipelineId())) {
                    PipelineWidgetVO pipelineWidgetGroup = wrapPipelineWidgetGroup(pipeline.getId(), pipeline.getName());
                    resultList.add(pipelineWidgetGroup);
                    break;
                }
            }
        } else {
            //编辑态
            for (PipelineDTO pipeline : pipelineDTOS) {
                JSONObject dataJson = JSONObject.parseObject(pipeline.getDataJson());
                if (!dataJson.containsKey(PUBLISHED_FLAG)) {
                    PipelineWidgetVO pipelineWidgetGroup = wrapPipelineWidgetGroup(pipeline.getId(), pipeline.getName());
                    resultList.add(pipelineWidgetGroup);
                }
            }
        }

        return ApiResult.valueOf(resultList);
    }

    @PostMapping(value = "/addFormula")
    @ResponseBody
    @ApiOperation(value = "执行公式", notes = "根据给定的公式计算值")
    public ApiResult<WidgetFormulaVO> addFormula(@RequestBody @ProjectAuth @Valid FormulaVO vo) {
        if (vo.getFormula().isEmpty()) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, WidgetFormulaVO.fail("公式不能为空。"));
        }
        WidgetFormulaVO widgetFormulaVO = widgetService.addOrUpdateFormula(vo.toHistoryDTO(), OPERATOR_ADD);
        return ApiResult.valueOf(widgetFormulaVO);
    }

    @PostMapping(value = "/updateFormula")
    @ResponseBody
    @ApiOperation(value = "更新公式", notes = "更新公式内容和数据集")
    public ApiResult<WidgetFormulaVO> updateFormula(@RequestBody @ProjectAuth @Valid FormulaVO vo) {
        if (vo.getFormula().isEmpty()) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, WidgetFormulaVO.fail("公式不能为空。"));
        }
        WidgetFormulaVO widgetFormulaVO = widgetService.addOrUpdateFormula(vo.toHistoryDTO(), OPERATOR_UPDATE);
        return ApiResult.valueOf(widgetFormulaVO);
    }

    @PostMapping(value = "/deleteFormula")
    @ResponseBody
    @ApiOperation(value = "删除公式")
    public ApiResult<WidgetFormulaVO> deleteFormula(@RequestBody @Valid FormulaIDVO vo) {
        WidgetFormulaVO widgetFormulaVO = widgetService.deleteFormula(vo.getFormulaId());
        return ApiResult.valueOf(widgetFormulaVO);
    }

    @PostMapping(value = "/queryFormulaList")
    @ResponseBody
    @ApiOperation(value = "查询公式", notes = "根据projectId查询公式")
    public ApiResult<FormulaHistoryVO> queryFormulasByProjectId(@RequestBody @ProjectAuth @Valid FormulaRequestVO vo) {
        return ApiResult.valueOf(widgetService.queryFormulasByProjectId(vo));
    }

    @PostMapping(value = "/queryById")
    @ResponseBody
    @ApiOperation(value = "查询可视化", notes = "根据id查询单个可视化")
    public ApiResult<WidgetVO> queryById(HttpServletRequest request, @RequestBody @Valid WidgetIDVO vo) {
        if (vo == null || vo.getId() == null) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        return ApiResult.valueOf(widgetService.queryById(vo.getId()).view());
    }

    @PostMapping(value = "/queryByTaskId")
    @ResponseBody
    @ApiOperation(value = "查询可视化列表", notes = "根据taskId查询可视化列表")
    public ApiResult<List<WidgetVO>> queryByTaskId(HttpServletRequest r, @RequestBody WidgetVO vo) {
        if (vo == null || vo.getTid() == null || !"task".equals(vo.getType())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        TaskDTO taskDTO = taskService.queryById(vo.getTid());
        if (null != taskDTO) {
            //校验用户是否有项目的读权限
            userProjectService.checkAuth(taskDTO.getProjectId(), ProjectAuthEnum.READ.getValue());
        }
        List<WidgetDTO> widgets = widgetService.queryTemplateByTaskId(vo.getTid());
        List<WidgetVO> result = widgets.stream().map(WidgetDTO::view).collect(Collectors.toList());
        return ApiResult.valueOf(result);
    }

    @PostMapping(value = "/deleteById")
    @Transactional
    @ResponseBody
    @ApiOperation(value = "删除可视化", notes = "根据id删除单个可视化")
    public ApiResult<Void> deleteById(HttpServletRequest r, @RequestBody @ProjectAuth @Valid WidgetIDVO vo) {
        widgetFavouriteService.deleteWidget(vo.getId());
        widgetService.delete(vo.getId());
        //TODO 如果被删除的是关联图表 需要更新parent
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping(value = "/getGlobalWidgets")
    @ResponseBody
    @ApiOperation(value = "获取全局的可视化图表", notes = "分页获取全局的可视化图表")
    public ApiResult<JSONObject> getGlobalWidgets(HttpServletRequest request,
                                                  @ProjectAuth(auth = ProjectAuthEnum.READ) @RequestBody @Valid WidgetPageVO vo) {
        return ApiResult.valueOf(widgetService.getGlobalWidgets(vo));
    }


    @PostMapping(value = "/getRelateWidgets")
    @ResponseBody
    @ApiOperation(value = "获取相关图表列表", notes = "获取相关图表列表")
    public ApiResult<RelateWidgetResultVO> getRelateWidgets(HttpServletRequest request,
                                                            @ProjectAuth(auth = ProjectAuthEnum.READ) @RequestBody @Valid RelateWidgetRequestVO vo) {
        if (vo.getIsFilterWidget()) {
            //可视化构建-筛选器控件 本身不提供渲染 但是可以驱动其他图表 重新渲染， 驱动关系由interactionJson 指定
            return ApiResult.valueOf(widgetService.getRelatedWidgetsWithinSpecificDataNode(vo));
        }else {
            return ApiResult.valueOf(widgetService.getRelateWidgetsInPipeline(vo));
        }
    }


    /**
     * 包装生成PipelineWidgetVO
     * @param pipelineId
     * @param pipelineName
     * @return
     */
    private PipelineWidgetVO wrapPipelineWidgetGroup(Long pipelineId, String pipelineName) {
        PipelineWidgetVO pw = new PipelineWidgetVO();
        pw.setPipelineId(pipelineId);
        pw.setGroupName(pipelineName);
        List<WidgetPackageVO> pipelineWidgets = Lists.newArrayList();
        for (TaskDTO task : taskService.queryByPipeline(pipelineId)) {
            List<WidgetVO> tempVOS = task.toWidgets().stream().map(WidgetDTO::view)
                    .collect(Collectors.toList());
            List<WidgetDTO> widgets = widgetService.queryTemplateByTaskId(task.getId());
            if (widgets.size() > 0) {
                tempVOS.addAll(widgets.stream().map(WidgetDTO::view)
                        .collect(Collectors.toList()));
            }
            pipelineWidgets.add(new WidgetPackageVO(task.getId(), task.getName(), tempVOS));
        }
        pw.setTaskWidgets(pipelineWidgets);
        return pw;
    }

}

